package com.mrzhou.web.util;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.mrzhou.core.tool.utils.ClassUtil;
import com.mrzhou.core.tool.utils.JacksonUtil;
import com.mrzhou.core.tool.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Controller层中自定义切面处理代理对象的参数
 * @author qiang.zhou
 */
@Slf4j
public class WebArgumentUtil {

    /**
     * 处理代理对象的方法参数
     * @param method
     * @param point
     * @return
     */
    public static Map<String, Object> handlerArgumentToMap(Method method, ProceedingJoinPoint point) { //
        Map<String, Object> map = new HashMap<>(16);
        // 防止出现空指针时导致框架处理线程失败
        if (method == null || point == null) {
            log.warn("处理代理方法参数失败; method: {}, point : {}", method, point);
            return map;
        }
        // 获取代理方法的参数组
        Object[] objects = point.getArgs();
        for (int i = 0; i < objects.length; i++) {
            // 获取代理方法的参数
            MethodParameter methodParameter = ClassUtil.getMethodParameter(method, i);
            // 当前参数是否有@PathVariable修饰, 有则返回
            PathVariable pathVariable = methodParameter.getParameterAnnotation(PathVariable.class);

            // @PathVariable参数直接跳过, 可从uri中查看
            if (pathVariable != null) {
                continue;
            }

            // 获取@RequestBody修饰的参数
            RequestBody requestBody = methodParameter.getParameterAnnotation(RequestBody.class);
            // 获取参数名称
            String parameterName = methodParameter.getParameterName();
            Object value = objects[i];
            if (requestBody != null) {
                if (value == null) {
                    map.put(parameterName, null);
                } else if (ClassUtil.isPrimitiveWrapper(value.getClass())) {
                    // 判断参数类型是否为基本数据类型或者是包装类
                    map.put(parameterName, value);
                } else {
                    // 将参数装换为json保存
                    map.put(parameterName, JacksonUtil.toJsonString(value));
                }
                continue;
            }

            // 处理未被任何注解修饰的参数
            if (value instanceof HttpServletRequest) {
                // 如果是HttpServletRequest
                map.putAll(((HttpServletRequest) value).getParameterMap());
                continue;
            } else if(value instanceof WebRequest) {
                map.putAll(((WebRequest) value).getParameterMap());
                continue;
            } else if(value instanceof HttpServletResponse) {
                continue;
            } else if(value instanceof MultipartFile) {
                map.put(parameterName, ((MultipartFile) value).getOriginalFilename());
                continue;
            }

            // @RequestParam修饰的参数
            RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class);
            String paramName = parameterName;
            if(null != value && null != requestParam && StringUtil.isNotBlank(requestParam.value())) {
                paramName = requestParam.value();
            }

            if(value == null) {
                map.put(paramName, null);
            } else if(ClassUtil.isPrimitiveWrapper(value.getClass())) {
                // 检查value是否为基本的数据类型及对应的包装类
                map.put(paramName, value);
            } else if(value instanceof InputStream) {
                map.put(paramName, "InputStream");
            } else if (value instanceof InputStreamSource) {
                map.put(paramName, "InputStreamSource");
            } else if(canJsonSerialize(value)) {
                // 最终处理value数据
                map.put(paramName, value);
            } else {
                map.put(paramName, "【警告】当前的参数无法序列化为json");
            }
        }
        return map;
    }

    private static boolean canJsonSerialize(Object json) {
        try{
            JacksonUtil.toJsonString(json);
            return true;
        } catch (Throwable e) {
            return false;
        }
    }

}
